package com.my.processor.test.three;


import com.google.auto.service.AutoService;
import com.my.processor.common.AbstractLogProcessor;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.util.Elements;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.Writer;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 *
 * 生成文件
 * @author timo 2021/2/19
 */
//@AutoService(Processor.class) :向javac注册我们这个自定义的注解处理器，这样，在javac编译时，才会调用到我们这个自定义的注解处理器方法。
//AutoService这里主要是用来生成
//META-INF/services/javax.annotation.processing.Processor.Processor文件的。如果不加上这个注解，那么，你需要自己进行手动配置进行注册，具体手动注册方法如下：
//1.创建一个
//META-INF/services/javax.annotation.processing.Processor.Processor文件，
//其内容是一系列的自定义注解处理器完整有效类名集合，以换行切割：
//  compile 'com.google.auto.service:auto-service:1.0-rc3'
//作者：Whyn
//链接：https://www.jianshu.com/p/d7567258ae85
//来源：简书
//著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
@AutoService(Processor.class)
public class MyProcessor3 extends AbstractLogProcessor {
//init(ProcessingEnvironment env):每个Annotation Processor必须***
//有一个空的构造函数 *。编译期间，init()会自动被注解处理工具调用，并传入ProcessingEnviroment参数，通过该参数可以获取到很多有用的工具类: Elements , Types , Filer **等等
//process(Set<? extends TypeElement> annoations, RoundEnvironment roundEnv):Annotation Processor扫描出的结果会存储进roundEnv中，可以在这里获取到注解内容，编写你的操作逻辑。注意,process()函数中不能直接进行异常抛出,否则的话,运行Annotation Processor的进程会异常崩溃,然后弹出一大堆让人捉摸不清的堆栈调用日志显示.
//getSupportedAnnotationTypes(): 该函数用于指定该自定义注解处理器(Annotation Processor)是注册给哪些注解的(Annotation),注解(Annotation)指定必须是完整的包名+类名(eg:com.example.MyAnnotation)
//getSupportedSourceVersion():用于指定你的java版本，一般返回：SourceVersion.latestSupported()。当然，你也可以指定具体java版本：
//return SourceVersion.RELEASE_7;
    private Filer mFiler;
    private Messager mMessager;
    private Elements mElementUtils;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        mFiler = processingEnvironment.getFiler();
        mMessager = processingEnvironment.getMessager();
        mElementUtils = processingEnvironment.getElementUtils();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(BindView.class.getCanonicalName());
        return annotations;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }


    @Override
    protected boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        System.out.println("Test log in MyProcessor.process");
        Set<? extends Element> bindViewElements = roundEnv.getElementsAnnotatedWith(BindView.class);
        for (Element element : bindViewElements) {
            // 在gradle的控制台打印信息
            log("className: " + element.getSimpleName().toString());
            // 创建main方法
            MethodSpec main = MethodSpec.methodBuilder("main")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(void.class)
                    .addParameter(String[].class, "args")
                    .addStatement("$T.out.println($S)", System.class, "自动创建的")
                    .build();
            // 创建HelloWorld类
            TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                    .addMethod(main)
                    .build();
            String packageName = processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString();
            try {
                JavaFile javaFile = JavaFile.builder(packageName, helloWorld)
                        .addFileComment(" This codes are generated automatically. Do not modify!")
                        .build();
                javaFile.writeTo(mFiler);
            } catch (IOException e) {
                e.printStackTrace();
            }

            //1.获取包名
            PackageElement packageElement = mElementUtils.getPackageOf(element);
            String pkName = packageElement.getQualifiedName().toString();
            log(String.format("MyProcessor3 package = %s", pkName));

            //2.获取包装类类型
            TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
            String enclosingName = enclosingElement.getQualifiedName().toString();
            log(String.format("MyProcessor3 enclosindClass = %s", enclosingElement));


            //因为BindView只作用于filed，所以这里可直接进行强转
            VariableElement bindViewElement = (VariableElement) element;
            //3.获取注解的成员变量名
            String bindViewFiledName = bindViewElement.getSimpleName().toString();
            //3.获取注解的成员变量类型
            String bindViewFiledClassType = bindViewElement.asType().toString();

            //4.获取注解元数据
            BindView bindView = element.getAnnotation(BindView.class);
            int id = bindView.value();
            log(String.format("MyProcessor3 %s %s = %d", bindViewFiledClassType, bindViewFiledName, id));
            //4.生成文件
//            createFile(enclosingElement, bindViewFiledClassType, bindViewFiledName, id);
            return true;
        }



        return false;
    }

    private void createFile(TypeElement enclosingElement, String bindViewFiledClassType, String bindViewFiledName, int id) {
        String pkName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
        log("MyProcessor3 file ==================" + pkName);
        try {
            JavaFileObject jfo = mFiler.createSourceFile(pkName + ".ViewBinding", new Element[]{});
            Writer writer = jfo.openWriter();
            writer.write(brewCode(pkName, bindViewFiledClassType, bindViewFiledName, id));
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private String brewCode(String pkName, String bindViewFiledClassType, String bindViewFiledName, int id) {
        StringBuilder builder = new StringBuilder();
        builder.append("package " + pkName + ";\n\n");
        builder.append("//Auto generated by apt,do not modify!!\n\n");
        builder.append("public class ViewBinding { \n\n");
        builder.append("public static void main(String[] args){ \n");
        String info = String.format("%s %s = %d", bindViewFiledClassType, bindViewFiledName, id);
        builder.append("System.out.println(\"" + info + "\");\n");
        builder.append("}\n");
        builder.append("}");
        return builder.toString();
    }

}
